package com.xinxing.learning.security.controller;

import com.xinxing.learning.security.entity.User;
import com.xinxing.learning.security.service.UserService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 通过注解 @PreAuthorize 实现权限校验的原数据是登录的时候获取到的用户角色信息 见: {@link User#getAuthorities()}
 * 通过资源配置实现URL权限验证的原数据是过滤器中每次从数据库动态加载的 见: {@link com.xinxing.learning.security.config.CustomerSecurityMetaSource#getAttributes(Object)}
 *
 * 因为用户信息是每次登录的时候加载一次然后生成授权信息保存到 Authentication 中，如何动态修改了？可以修改数据库后退出当前用户重新登录一次就能加载到最新的权限信息。
 * 但是每次都需要登录，如果是因为账号被别人盗用，想通过修改权限下来降低风险，盗用的人是不会退出登录的。这个时候可以动态的修改方式见 {@link this#updateUserInfo()}
 * 还可以优化成动态监听的情况，比如在修改用户信息完成后发送一个事件，系统启动的时候启动一个监听改事件的监听器，在监听器中重新加载登录的用户信息。
 *
 * https://blog.csdn.net/weixin_44981485/article/details/106647197
 * https://cloud.tencent.com/developer/article/1598948
 * https://blog.csdn.net/fu_huo_1993/article/details/88224938
 */
@RestController
public class DemoController {
    private final UserService userService;

    public DemoController(UserService userService) {
        this.userService = userService;
    }

    @PreAuthorize("hasAuthority('efg')")
    @GetMapping("/admin/demo")
    public String admin() {
        return "security dynamic authorize admin demo";
    }

    @GetMapping("/user/demo")
    public String user() {
        return "security dynamic authorize user demo";
    }

    @GetMapping("/guest/demo")
    public String guest() {
        return "security dynamic authorize guest demo";
    }

    @GetMapping("/demo")
    public String demo() {
        return "security dynamic authorize demo";
    }

    /**
     * 动态修改登录用户的权限信息
     *
     * @return
     */
    @GetMapping("/updateUserInfo")
    public String updateUserInfo() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User principal = (User) authentication.getPrincipal();
        UserDetails userDetails = userService.loadUserByUsername(principal.getUsername());
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        return authentication.toString();
    }

    @GetMapping("/getAuthResult")
    public String getAuthResult() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication.toString();
    }
}
